package edu.xust.core;


import com.fasterxml.jackson.databind.ObjectMapper;
import edu.xust.config.RequestBody;
import edu.xust.config.Resource;
import edu.xust.config.ResponseBody;
import edu.xust.utils.BeanUtils;
import edu.xust.utils.CommandUtils;
import edu.xust.utils.GetRequestJsonUtils;
import edu.xust.utils.ReflectUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.stream.Collectors;

@WebServlet("/")
public class DispatcherServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ObjectMapper objectMapping = new ObjectMapper();
        response.setContentType("text/html;charset=utf-8");
        request.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        Object paramObject = null;//默认参数为空只能用于判断

        // String requestURI = request.getRequestURI();
        String servletPath = request.getServletPath();//得到地址 /studentController/getStudents
        //String contextPath = request.getContextPath();       //得到地址        /TestWeb
        // String url = request.getRequestURL().toString();
        //System.out.println(requestURI);//得到地址 /TestWeb/studentController/getStudents
        if (servletPath.length() <= 1) {
            out.write("非法操作");
            out.flush();
            out.close();
            return;
        }
        System.out.println(servletPath);
        String[] requestMappings = servletPath.substring(1).split("/");
        Class controllerClass = InitMapping.controllerMaps.get("/" + requestMappings[0]);

        //requestMappings[0]得到是是controller对应的url 通过这个url 可以找到对应的时候controller 类，是因为启动项目的，listener中进行了存储

        Method method = null;
        if (requestMappings.length == 2) {
            method = InitMapping.methodMaps.get("/" + requestMappings[1]);
            if(method==null) {
                out.write("非法操作");
                out.flush();
                out.close();
            }
            //如果方法url匹配得到method对象，判断该method对象是否有参数
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length>0) {
                System.out.println(method.getName());
                System.out.println(parameterTypes[0].getName());
                try {
                    if(method.getName().equals("upload")){
                        paramObject = request;
                    }else {
                        paramObject = parameterTypes[0].newInstance();//根据参数类型new一个实例
                        //所有前端提交的数据都保存在request的 paramterMap中，将这个map中的值对应的拷贝给参数对象就可以完成调用

                        Map<String, String[]> parameterMap = request.getParameterMap();
                        //如果 参数上有注解
                        Class methodParameterNamesByAnnotation = ReflectUtils.getMethodParameterNamesByAnnotation(method, RequestBody.class)[0];
                        if (methodParameterNamesByAnnotation.equals(RequestBody.class)) {
                            String paramJson = GetRequestJsonUtils.getRequestJsonString(request);//强制要求前端提交一个data为key json数据


                            paramObject = objectMapping.readValue(paramJson, parameterTypes[0]);// 利用jackson工具将json字符串转换成对应的参数对象
                        } else {
                            BeanUtils.copyPropertiesFromRequestMap(parameterMap, paramObject);
                        }
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }

        } else {
            //如果发现有参数 只需要url中方法名的部分匹配即可，例如 url存放的是 /getStudentById/{id} 只需要/getStudentById/匹配即可
            //method
            Map<String, Method> collect = InitMapping.methodMaps.entrySet().stream().filter(entry -> entry.getKey().toString().indexOf("/" + requestMappings[1] + "/") >= 0)
                    .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
            method = collect.values().iterator().next();//将过滤后的map转换成迭代器（iterator 循环）并迭代一次取出第一个
        }
        if (controllerClass == null || method == null) {

            out.write("非法操作2");
            out.flush();
            out.close();

        } else {
            String string = "";
            try {
                Object o = controllerClass.newInstance();
                initField(o);

                Object returnResult = null;
                //InitMapping.sqlSession.clearCache();
                //方法调用有三种情况 1 无参  2.有普通类型参数1个  3有对象参数1个
                if (requestMappings.length > 2) {// 如果url根据/分隔解析的数组长度大于2 说明路径上有传参
                    String params = requestMappings[2];//第三部分为参数
                    Parameter parameter = method.getParameters()[0];

                    Object obj = CommandUtils.changeType(params, parameter.getType());

                    returnResult = method.invoke(o, obj);
                } else if (paramObject != null) {
                    returnResult = method.invoke(o, paramObject);
                } else {
                    returnResult = method.invoke(o, null);
                }
                if (method.isAnnotationPresent(ResponseBody.class)) {
                    string = objectMapping.writeValueAsString(returnResult);
                } else {
                    string = returnResult.toString();
                }
                InitMapping.sqlSession.flushStatements();
                InitMapping.sqlSession.commit();
                out.write(string);
                out.flush();

            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } finally {
                out.close();
            }
        }

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    /**
     * 处理前端提交过来的多个参数封装到对象中
     */
    private void initObjectParam() {

    }

    /**
     * 将controller 对象的 成员变量 中有resource （目前支持service）从servicemap中找到匹配的
     * @param o
     */
    private void initField(Object o) throws IllegalAccessException {
        Class<?> clazz = o.getClass();
        Field[] fds = clazz.getDeclaredFields();
        for(Field fd :fds){
            //拿到成员变量 有resource注解的值的名字
            if(fd.isAnnotationPresent(Resource.class)){
                String value = fd.getName();

                Object fdObj = InitMapping.serviceMaps.get(value);
                if (fdObj == null){
                    fdObj = InitMapping.daoMaps.get(value);
                }
                if (fdObj == null){
                    fdObj = InitMapping.mapperMaps.get(value);
                }
                fd.setAccessible(true);
                fd.set(o,fdObj);
                for (Field field : fdObj.getClass().getDeclaredFields()){
                    if(field.isAnnotationPresent(Resource.class)){
                        initField(fdObj);
                    }
                }
            }
        }
    }
}
